#include"jiajia_stream.h"

static void _JiaJia_stream_bufup(JiaJia_stream_pt stream){
    unicode *old_buf = (unicode_t *)stream->buf;
    size_t old_buf_size = stream->buf_size;

    size_t new_buf_size = 0;
    new_buf_size = old_buf_size * 2 * sizeof(unicode_t);

    /* 扩大为原来的两倍。 */
    unicode *new_buf = Yu_alloc(new_buf_size, NULL, NULL);

    /* 将旧缓存移至新缓存。 */
    uint32_t top = 0;
    uint32_t cur = stream->back;
    while(old_buf[cur] != YU_UNICODE_EOF){
        new_buf[top++] = old_buf[cur];
        cur = (cur < old_buf_size - 1)? (cur + 1):0;
    }
    new_buf[top] = YU_UNICODE_EOF;

    stream->buf = (char *)new_buf;
    stream->buf_size = new_buf_size;

    if(stream->forward >= stream->back)
        stream->forward = stream->forward - stream->back;
    else
        stream->forward =
            stream->buf_size - stream->back + stream->forward;

    stream->back = 0;

    Yu_free(old_buf);
}

static void _JiaJia_stream_load(JiaJia_stream_pt stream){
    if(feof(&stream->text))
        return ;

    size_t it1 = 0;
    size_t it2 = 0;
    size_t it_end = 0;

    if(stream->forward >= stream->back){
        if((stream->buf_size - stream->forward + stream->back) <= 1)
            JiaJia_stream_bufup(stream);
    }else{
        if((stream->back - stream->forward) <= 1)
            JiaJia_stream_bufup(stream);
    }

    it1 = stream->forward;
    it2 = (stream->back == 0)? (stream->buf_size - 1):(stream->back - 1);
                
    it_end = Yu_text_itread(&stream->text, stream->buf, stream->buf_size, it1, it2);
    stream->buf[it_end] = YU_UNICODE_EOF;
}

static int8_t _JiaJia_stream_check(const char *filename){
    const char *pot = strrchr(filename, '.');
    if(pot == NULL)
        return 0;

    return strcmp(pot, ".jiajia") == 0;
}

extern void JiaJia_stream_meminit(void *pt, size_t size){
    JiaJia_stream_pt stream = (JiaJia_stream_pt)pt;
    size = size/sizeof(JiaJia_stream_t);
    size_t i = 0;
    for(; i < size; ++i){
        _JiaJia_stream_meminit_once(&stream[i]);
    }
}

extern void JiaJia_stream_memfree(void *pt, size_t size){
    JiaJia_stream_pt stream = (JiaJia_stream_pt)pt;
    size = size/sizeof(JiaJia_stream_t);
    size_t i = 0;
    for(; i < size; ++i){
        if(stream[i].buf != NULL)
            Yu_free(stream[i].buf);

        Yu_text_close(&(stream[i].text));
        _JiaJia_stream_meminit_once(&stream[i]);
    }
}

extern int32_t JiaJia_stream_open(JiaJia_stream_pt stream, const char *file_name){
    if(stream == NULL ||
       file_name == NULL)
        return JIAJIA_STREAM_OPEN_NOEXIT;

    if(!_JiaJia_stream_check(file_name))
        return JIAJIA_STREAM_OPEN_NOTYPE;

    JiaJia_stream_close(stream);
        
    if(!Yu_text_read_open(&stream->text, file_name, YU_TEXT_UTF8))
        return JIAJIA_STREAM_OPEN_FAIL;

    stream->buf = Yu_alloc(sizeof(char)*_JIAJIA_STREAM_MIN_BUF_SIZE, NULL, NULL);
    stream->buf_size = _JiaJia_STREAM_MIN_BUF_SIZE;
    stream->buf[0] = YU_UNICODE_EOF;

    return JIAJIA_STREAM_OPEN_SUCC;
}

extern unicode JiaJia_stream_next_unicode(JiaJia_stream_pt stream){
    if(stream->buf[stream->forward] == YU_UNICODE_EOF){
        /* 进行 u32 文本加载。 */
        _JiaJia_u32stream_load(stream);
    }

    /* 
       若 forward 指向 EOF 字符，证明文件已经全部加载完毕；
       否则，将 forward 后移一步。
     */
    unicode_t c32 = stream->buf[stream->forward];
    if(c32 != YU_UNICODE_EOF)
        stream->forward = (stream->forward < stream->buf_size - 1)? (strea->forward + 1):0;

    return c32;
}

extern void JiaJia_stream_back_unicode(JiaJia_stream_pt stream){
    if(stream->back != stream->forward){
        stream->forward = (stream->forward > 0)? (stream->forward - 1):(stream->buf_size - 1);
    }
}

extern unicode_t JiaJia_stream_throw_unicode(JiaJia_stream_pt stream){
    unicode_t c32 = YU_UNICODE_EOF;
    if(stream->back != stream->forward){
        c32 = stream->buf[stream->back];
        stream->back = (stream->back < stream->buf_size - 1)? (stream->back + 1):0;

        /* 计算行和列 */
        if(c32 == '\n'){
            ++jjfile->rows;
            jjfile->cols = 1;
        }else{
            ++jjfile->cols;
        }
    }

    return c32;
}

extern void JiaJia_stream_throw_string(JiaJia_stream_pt stream, Yu_string_pt str){
    unicode_t buf[256];
    uint32_t buf_top = 0;
    while((buf[buf_top] = JiaJia_stream_throw_unicode(stream)) != YU_UNICODE_EOF){
        ++buf_top;
        if(buf_top == 256){
            Yu_string_append_u32_buf(str, buf, buf_top);
            buf_top = 0;
        }
    }

    if(buf_top != 0)
        Yu_string_append_u32_buf(str, buf, buf_top);
}
